#pragma once

#include "Awaiter.h"

namespace AsyncLib
{

template<typename T>
class CFuture;

template<typename T>
class CPromiseBase
{
public:
	CPromiseBase() : m_pState(std::make_shared<CAssocState<T>>())
	{
	}

	~CPromiseBase()
	{
		// All stack variables have been destructed here
		m_pState->Complete();
	}

	CPromiseBase(const CPromiseBase&) = delete;
	CPromiseBase& operator=(const CPromiseBase&) = delete;

	CFuture<T> get_return_object() const
	{
		return m_pState;
	}

	std::suspend_never initial_suspend() const noexcept
	{
		return {};
	}

	std::suspend_never final_suspend() const noexcept
	{
		return {};
	}

	void unhandled_exception() const
	{
		m_pState->SetException(std::current_exception());
	}

	template<typename T2>
	CAwaiter<T2> await_transform(const CFuture<T2>& future) const
	{
		return CFuture<T2>::GetState(future);
	}

	template<typename T2>
		requires requires(T2 t) { t.GetFuture(); }
	auto await_transform(const T2& t) const
	{
		return await_transform(t.GetFuture());
	}

protected:
	std::shared_ptr<CAssocState<T>> m_pState;
};

template<typename T>
class CPromise : public CPromiseBase<T>
{
public:
	template<typename T2>
	void return_value(T2&& value)
	{
		this->m_pState->SetValue(std::forward<T2>(value));
	}
};

template<>
class CPromise<void> : public CPromiseBase<void>
{
public:
	void return_void()
	{
		this->m_pState->SetValue();
	}
};

} // namespace AsyncLib
